// ----------------------------------------------------------------------
// dataStream.h
//
/* Description:
	A chunk of data that can be read and written to.
*/
// ------------------------------------------------------------------------

#include <stack>
#include <iostream>
#include <fstream>
#include "zlib/zlib.h"
#ifndef _H_DATASTREAM_
#define _H_DATASTREAM_

 enum direction { //Used for seeking in the dataStream
	cur = 0,
	beg = 1,
	end = 2
};

class dataStream {
public:
	dataStream(); //Creates empty dataStream
	dataStream(char* buf, unsigned int len); //Creates dataStream based on existing data 
	dataStream(unsigned int len); //Creates a dataStream and allocates len bytes initialy

	//Disk I/O
	void save(std::string fileName); //Save to a file on disk
	void load(std::string fileName); //Load a file from disk to the datastream

	//Compression
	bool uncompressData(unsigned int chunks = 1024); //Will uncompress the entire dataStream. Compressed data will be deleted and dataStream will be replaced with the uncompressed data.
	bool compressData(); //Compress all the data. Old data will be replaced with the expanded data
	//Buffer handling
	unsigned int getLength(); //Get size of the buffer
	unsigned int tellp(); //returns put position
	unsigned int tellg(); //returns get position
	char* getBuffer(); //Returns a pointer to the buffer
	bool seekp(unsigned int pos, direction dir = beg); //Seek put position
	bool seekg(unsigned int pos, direction dir = beg); //Seek get position

	//Data operations
	unsigned char readByte();
	unsigned short readWord();
	unsigned int readDword();
	void readBytes(char* dest, unsigned int len);
	dataStream* readBytes(unsigned int len); //Reads bytes, but makes a new dataStream and readsa the bytes into it
	float readFloat(); 
	bool readBool();
	bool readBool4(); //Read 4 byte bool
	std::string readString();
	template <class T>
	T read() {
		if (checkReadOverflow(sizeof(T))) {
		return 0x00;
		}
		T retValue = *(T*)(dataBuffer + gPos);
		gPos += sizeof(T);
		return retValue;
	}

	// Writing
	void writeByte(unsigned char value);
	void writeBool4(bool value);
	void writeWord(unsigned short value);
	void writeDword(unsigned int value);
	void writeInt(int value);
	void writeFloat(float value);
	void writeBytes(char* value, unsigned len);
	void writeString(std::string value);
	template <class T>
	void write(T value) {
		if (checkWriteOverflow(sizeof(T))) { //Not enough room
			expand(sizeof(T)); //Expand the buffer
		}
		*(T*)(dataBuffer + pPos) = value;
		pPos += sizeof(T);
	}

private:
	unsigned int pPos, gPos, length; //Put Position, Get Position, Length of data
	char* dataBuffer; //The data

	//Support functions
	bool checkWriteOverflow(unsigned int nBytes); //Checks if there is enough room to write nBytes to the buffer
	bool offsetInRange(unsigned int offset); //Checks if the specified offset is in range of the buffer
	void expand(unsigned int bytes); //Expand the buffer by bytes number of bytes
	bool checkReadOverflow(unsigned int nBytes); //Checks if nBytes can be read from the stream without going past the end of the buffer

	//Used for jumping around the data
	std::stack<unsigned int> positionStack; 


};

#endif